#ifndef __STREAM_REGISTER_CLASSES_H__
#define __STREAM_REGISTER_CLASSES_H__

#include "../../gamma/src/gamma.h"
#include "./includes/file_pointer_pack.h"
#include "./includes/register_pack.h"
#include "documentscontainer.h"
#include "document.h"



namespace invertedindex{

class StreamRegister : public Register{
	private:
		unsigned int wordId;
		DocumentsContainer* dc;

	public:
		StreamRegister(unsigned int wordId){
			this->wordId  = wordId;
			dc = new DocumentsContainer();
		}

		StreamRegister(const StreamRegister& sr){
			this->wordId  = sr.getWordId();
			this->dc=sr.getDocumentsContainer();
		}

		~StreamRegister(){
			delete dc;
		}

		void setWordId(unsigned int id){ this->wordId = id; }
		unsigned int getWordId()const{ return this->wordId;  }
		DocumentsContainer* getDocumentsContainer()const{
			return (new DocumentsContainer(*dc));
		}
		void setDocumentsContainer(const DocumentsContainer& dc){
			delete this->dc;
			this->dc=new DocumentsContainer(dc);
		}

		void exportDocumentsContainer(DocumentsContainer& dc){
			dc.setFields(*(this->dc));
		}

		bool insertDocument(const Document& document){
			return dc->insertDocument(document);
		}

		bool removeDocument(const Document& document){
			return dc->removeDocument(document);
		}

		Register* duplicate()const{
			return (new StreamRegister(*this));
		}

		void setFields(const Register& b2){
			const StreamRegister& temp = dynamic_cast<const StreamRegister&>(b2);
			delete dc;
			this->wordId = temp.getWordId();
			dc = temp.getDocumentsContainer();
		}

		unsigned int getSize()const {
			/* value,len cadena,cadena */
			unsigned int size=0;
			size+=2*sizeof(unsigned int);
			
			
			
			std::list<unsigned int*> *values = new std::list<unsigned int*>();
			unsigned int docCount = dc->getDocumentCount();
			if(docCount == 0)
			{
				std::cout<<"DOC COUNT = 0"<<std::endl;
				exit(-1);
			}
			values->push_back(new unsigned int(docCount));

			unsigned int aux;
			unsigned int lastDocId=0;
			unsigned int lastPos;
			for(unsigned int i=0;i<docCount;i++){
				aux=dc->getIdOfDocument(i).second;
				aux-=lastDocId;
				lastDocId+=aux;
				if(aux == 0)
				{
					std::cout<<"ID DOCUMENTO = 0"<<std::endl;
					exit(-1);
				}
				values->push_back(new unsigned int(aux));
				
				aux=dc->getNumberOfOccurrencesInDocument(i).second;
				if(aux == 0)
				{
					std::cout<<"NUMBER OF OCCURRENCES IN DOCUMENT = 0"<<std::endl;
					exit(-1);
				}				
				values->push_back(new unsigned int(aux));
				lastPos=0;
				for(unsigned int j=0; j<dc->getNumberOfOccurrencesInDocument(i).second;j++){
					aux=dc->getOccurrenceInDocument(i,j).second;
					aux-=lastPos;
					lastPos+=aux;
					if(aux == 0)
					{
						std::cout<<"OFFSET IN DOCUMENT = 0"<<std::endl;
						exit(-1);
					}
					values->push_back(new unsigned int(aux));
				}
			}
			Gamma::Gamma *myGamma = new Gamma::Gamma(values);
			std::list<unsigned char*> *gammas = myGamma->getGammas();
			
			size+=gammas->size();
			delete myGamma;

			return size;
		};

		bool operator<(const Register& b2)const{
			const StreamRegister& temp = dynamic_cast<const StreamRegister&>(b2);
			return (this->wordId<temp.getWordId());
		}

		std::ostream& toOstream(std::ostream& out)const{
			out << "\t\twordId: " << this->wordId << std::endl;
			out << "\t\tnro docs: " << dc->getDocumentCount() << std::endl;
			for(unsigned int i = 0; i<dc->getDocumentCount(); i++){
				out << "\t\t  idDoc["<<i<<"]: " << dc->getIdOfDocument(i).second << std::endl;
				out << "\t\t  numOcc: " << dc->getNumberOfOccurrencesInDocument(i).second<< std::endl;
				for(unsigned int j=0; j<dc->getNumberOfOccurrencesInDocument(i).second;j++){
					out << "\t\t\tpos["<<j<<"]: " <<dc->getOccurrenceInDocument(i,j).second <<std::endl;
				}
			}
			return out;
		}
};


/* Clase necesaria para poder levantar y bajar a disco */
class StreamRegisterFactory : public RegisterFactory{
	public:
		StreamRegisterFactory(){}
		~StreamRegisterFactory(){}

		/* Dado un array de bytes devuelve un registro KeyVal */
		Register* operator ()(char* data)const{
			char* temp = data;
			unsigned int docCount;
			unsigned int charCount;
			unsigned int wordId;	
			memcpy(&wordId,temp,sizeof(unsigned int));		
			temp += sizeof(unsigned int);
			
			
			memcpy(&charCount,temp,sizeof(unsigned int));		
			temp += sizeof(unsigned int);

			StreamRegister* ret = new StreamRegister(wordId);

			std::list<unsigned char*> *gammas = new std::list<unsigned char*>();
			for(unsigned int j=0; j<charCount; j++)
			{
				gammas->push_back(new unsigned char(*(temp+j)));
			}
			temp+=charCount;
			Gamma::Gamma *myGamma = new Gamma::Gamma(gammas);
			std::list<unsigned int*> *values = myGamma->getDistancias();
			std::list<unsigned int*>::iterator it;
			it = values->begin();
			
			docCount=**it;
			it++;
	
			unsigned int docId;
			unsigned int numberOfOcc;
			unsigned int position;
			unsigned int lastDocId=0;
			unsigned int lastPos=0;
			
			for(unsigned int i = 0; i<docCount; i++){
				docId=**it;
				it++;
				
				docId+=lastDocId;
				lastDocId=docId;
				Document dc(docId);
				//memcpy(&numberOfOcc,temp,sizeof(unsigned int));		
				//temp += sizeof(unsigned int);
				numberOfOcc=**it;
				it++;
				
				lastPos=0;
				for(unsigned int j=0; j<numberOfOcc;j++){
					//memcpy(&position,temp,sizeof(unsigned int));
					position=**it;
					it++;
					position+=lastPos;
					lastPos=position;		
					//temp += sizeof(unsigned int);
					dc.insertPosition(position);
				}
				ret->insertDocument(dc);
			}

			delete myGamma;
			return ret;
		}
		
		/* Dado un registro y un array de bytes serializa la clase KeyVal en el array de bytes */
		char* operator ()(const Register& reg,char* data)const{
			const StreamRegister& kvreg = dynamic_cast<const StreamRegister&>(reg);
			char* temp = data;
			DocumentsContainer* dc= kvreg.getDocumentsContainer();
			std::list<unsigned int*> *values = new std::list<unsigned int*>();

			unsigned int wordId = kvreg.getWordId();
			memcpy(temp,(char*)&wordId,sizeof(unsigned int));
			temp+=sizeof(unsigned int);

			unsigned int docCount = dc->getDocumentCount();
			values->push_back(new unsigned int(docCount));
			//memcpy(temp,(char*)&docCount,sizeof(unsigned int));
			//temp+=sizeof(unsigned int);

			unsigned int aux;
			unsigned int lastDocId=0;
			unsigned int lastPos;
			for(unsigned int i=0;i<docCount;i++){
				aux=dc->getIdOfDocument(i).second;
				aux-=lastDocId;
				lastDocId+=aux;
				//memcpy(temp,(char*)&aux,sizeof(unsigned int));
				//temp+=sizeof(unsigned int);
				values->push_back(new unsigned int(aux));
				
				aux=dc->getNumberOfOccurrencesInDocument(i).second;
				values->push_back(new unsigned int(aux));
				//memcpy(temp,(char*)&aux,sizeof(unsigned int));
				//temp+=sizeof(unsigned int);
				lastPos=0;
				for(unsigned int j=0; j<dc->getNumberOfOccurrencesInDocument(i).second;j++){
					aux=dc->getOccurrenceInDocument(i,j).second;
					aux-=lastPos;
					lastPos+=aux;
					//memcpy(temp,(char*)&aux,sizeof(unsigned int));
					//temp+=sizeof(unsigned int);
					values->push_back(new unsigned int(aux));
				}
			}
			Gamma::Gamma *myGamma = new Gamma::Gamma(values);
			std::list<unsigned char*> *gammas = myGamma->getGammas();
			
			unsigned int gammasLength = gammas->size();
			memcpy(temp,(char*)&gammasLength,sizeof(unsigned int));
			temp+=sizeof(unsigned int);
			
			std::list<unsigned char*>::iterator it;
			for(it = gammas->begin(); it != gammas->end(); it++)
			{
				memcpy(temp,(char*)*it,sizeof(unsigned char));
				temp+=sizeof(unsigned char);
			}
			delete myGamma;
			delete dc;
			return temp;
		}

		StreamRegisterFactory* duplicate()const
		{
			return new StreamRegisterFactory;
		}
};

}

#endif

